package org.zoomdev.zoom.asm;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

public class ClassBuilder {
    // 类基本信息
    private final String className;
    private final List<Class<?>> interfaces = new ArrayList<>();
    private final List<String> genericSignatures = new ArrayList<>();
    // 类成员
    private final Map<String, FieldBuilder> fields = new HashMap<>();
    private final List<MethodBuilder> methods = new ArrayList<>();
    // 注解支持
    private final List<AnnotationBuilder> annotations = new ArrayList<>();
    // 内部类支持
    private final List<ClassBuilder> innerClasses = new ArrayList<>();
    private final Type classType;
    private Class<?> superClass = Object.class;
    private int access = Opcodes.ACC_PUBLIC;
    // 源文件信息
    private String sourceFile;
    private String sourceDebug;

    private Type superClassType;

    public ClassBuilder(String className) {
        this.className = className;
        this.classType = Type.getObjectType(className);
        this.superClassType = Type.getType(Object.class);
    }

    private String signature; // 类签名

    public ClassBuilder withSignature(String signature) {
        this.signature = signature;
        return this;
    }


    // === 类基本信息设置 ===

    public ClassBuilder withAccess(int access) {
        this.access = access;
        return this;
    }

    public ClassBuilder extend(Class<?> superClass) {
        this.superClass = superClass;
        this.superClassType = Type.getType(superClass);
        return this;
    }

    public ClassBuilder implement(Class<?> interfaceClass) {
        interfaces.add(interfaceClass);
        return this;
    }

    public ClassBuilder withGenericSignature(String signature) {
        genericSignatures.add(signature);
        return this;
    }

    public ClassBuilder withSource(String sourceFile) {
        this.sourceFile = sourceFile;
        return this;
    }

    public ClassBuilder withDebugInfo(String sourceDebug) {
        this.sourceDebug = sourceDebug;
        return this;
    }

    // === 注解支持 ===

    public AnnotationBuilder addAnnotation(String descriptor) {
        AnnotationBuilder annotation = new AnnotationBuilder(descriptor);
        annotations.add(annotation);
        return annotation;
    }

    public AnnotationBuilder addAnnotation(Class<?> annotationClass) {
        String descriptor = Type.getDescriptor(annotationClass);
        return addAnnotation(descriptor);
    }

    // === 内部类支持 ===

    public ClassBuilder addInnerClass(String innerClassName, Consumer<ClassBuilder> configurator) {
        ClassBuilder innerClass = new ClassBuilder(className + "$" + innerClassName)
                .withAccess(Opcodes.ACC_STATIC)
                .extend(Object.class);
        configurator.accept(innerClass);
        innerClasses.add(innerClass);
        return this;
    }

    // === 字段管理 ===

    public ClassBuilder addField(String fieldName, Consumer<FieldBuilder> configurator) {
        FieldBuilder fieldBuilder = new FieldBuilder(this, fieldName);
        configurator.accept(fieldBuilder);
        fields.put(fieldName, fieldBuilder);
        return this;
    }

    public ClassBuilder addField(String fieldName, int access, Class<?> type) {
        FieldBuilder field = new FieldBuilder(this, fieldName).withType(type).withAccess(access);
        fields.put(fieldName, field);
        return this;
    }

    // === 方法管理 ===

    public ClassBuilder createMethod(String methodName, Consumer<MethodBuilder> configurator) {
        MethodBuilder methodBuilder = new MethodBuilder(this, methodName);
        configurator.accept(methodBuilder);
        methods.add(methodBuilder);
        return this;
    }

    public ClassBuilder createConstructor(Consumer<MethodBuilder> configurator) {
        return createMethod("<init>", method -> {
            method.withAccess(Opcodes.ACC_PUBLIC)
                    .returns(void.class);
            configurator.accept(method);
        });
    }

    public ClassBuilder addStaticInitializer(Consumer<MethodBuilder> configurator) {
        return createMethod("<clinit>", method -> {
            method.withAccess(Opcodes.ACC_STATIC)
                    .returns(void.class);
            configurator.accept(method);
        });
    }

    // === 便捷方法 ===

    public ClassBuilder addDefaultConstructor() {
        return createConstructor(method -> method
                .addInstruction(Instructions.loadThis())
                .addInstruction(Instructions.invokeConstructor(superClass))
                .addInstruction(Instructions.returnValue()));
    }

    public ClassBuilder addGetter(String fieldName, Class<?> fieldType) {
        String methodName = "get" + capitalize(fieldName);
        return createMethod(methodName, method -> method
                .withAccess(Opcodes.ACC_PUBLIC)
                .returns(fieldType)
                .addInstruction(Instructions.loadThis())
                .addInstruction(Instructions.getField(className, fieldName, fieldType))
                .addInstruction(Instructions.returnValue()));
    }

    public ClassBuilder addSetter(String fieldName, Class<?> fieldType) {
        String methodName = "set" + capitalize(fieldName);
        return createMethod(methodName, method -> method
                .withAccess(Opcodes.ACC_PUBLIC)
                .params(fieldType)
                .returns(void.class)
                .addInstruction(Instructions.loadThis())
                .addInstruction(Instructions.loadArg(0))
                .addInstruction(Instructions.putField(className, fieldName, fieldType))
                .addInstruction(Instructions.returnValue()));
    }

    // === 构建类 ===

    public byte[] build() {
        // 创建ClassWriter，自动计算栈帧和最大栈/本地变量表
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);

        // 类头信息
        cw.visit(Opcodes.V1_8, access, className, signature,
                Type.getInternalName(superClass),
                getInterfaceNames());

        // 泛型签名
//        if (!genericSignatures.isEmpty()) {
//            for (String signature : genericSignatures) {
//                cw.visitGenericSignature(signature);
//            }
//        }

        // 源文件信息
        if (sourceFile != null) {
            cw.visitSource(sourceFile, sourceDebug);
        }


        // 添加字段
        for (FieldBuilder fieldBuilder : fields.values()) {
            fieldBuilder.build(cw);
        }

        // 确保有构造函数
        if (!hasConstructor()) {
            addDefaultConstructor();
        }

        // 添加方法
        for (MethodBuilder methodBuilder : methods) {
            methodBuilder.build(cw);
        }

        // 添加内部类
        for (ClassBuilder innerClass : innerClasses) {
            innerClass.build(); // 递归构建内部类
            cw.visitInnerClass(
                    innerClass.className,
                    className,
                    innerClass.className.substring(className.length() + 1),
                    innerClass.access);
        }

        cw.visitEnd();
        return cw.toByteArray();
    }

    // === 访问器 ===

    public String getClassName() {
        return className;
    }

    public Class<?> getSuperClass() {
        return superClass;
    }

    public FieldBuilder getField(String fieldName) {
        return fields.get(fieldName);
    }

    public Type getClassType() {
        return classType;
    }

    public Type getSuperClassType() {
        return superClassType;
    }

    // === 内部辅助方法 ===

    private boolean hasConstructor() {
        for (MethodBuilder method : methods) {
            if ("<init>".equals(method.getMethodName())) {
                return true;
            }
        }
        return false;
    }

    private String[] getInterfaceNames() {
        String[] interfaceNames = new String[interfaces.size()];
        for (int i = 0; i < interfaces.size(); i++) {
            interfaceNames[i] = Type.getInternalName(interfaces.get(i));
        }
        return interfaceNames;
    }

    private String capitalize(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        return Character.toUpperCase(str.charAt(0)) + str.substring(1);
    }

}
